package cool.ale.errorMessage.util;

import cool.ale.errorMessage.config.redis.AleErrorMessageRedisService;
import cool.ale.errorMessage.constant.AleErrorMessageConst;
import cool.ale.errorMessage.properties.AleErrorMessageBaseProperties;
import cool.ale.errorMessage.constant.AleErrorMessageRedisLuaTemplateConst;
import cool.ale.util.batch.InBatchUtil;
import cool.ale.util.bean.AleBeanUtil;
import cool.ale.util.file.PropertiesUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 加载业务错误信息工具类
 *
 * @author dujlc
 * @date 2022/12/3 18:48
 * @see
 * @since 1.0
 */
@Slf4j
@Component
public class ErrorMsgUtil {

    /**
     * 业务日志属性类
     */
    @Autowired
    private AleErrorMessageBaseProperties aleErrorMessageBaseProperties;

    /**
     * redis服务类
     */
    @Autowired
    private AleErrorMessageRedisService aleErrorMessageRedisService;

    /**
     * 加载当前生效的国际化文本生效语言以及返回相应文件的业务异常map集合
     * @return
     */
    public ConcurrentHashMap<String, String> loadEffectLanguageAndGetErrorMap(){
        // 1、获取当前国际化文本生效的语言
        String effectLanguage = this.getEffectLanguage();

        // 2、找出符合条件的文件并放到一个map中
        // 2.1、根据数据库中配置生效的国际化文本类型将加载到所有的文件筛选出需要的文件
        // 2.2、解析出符合条件的文件内容并放到一个map中
        ConcurrentHashMap<String, String> errorMap = this.getErrorMap(effectLanguage);
        if (AleBeanUtil.isNull(errorMap)){
            return null;
        }
        return errorMap;
    }

    /**
     * 将业务异常描述全部放入redis数据库中
     * @param errorMap      异常信息map
     * @return      返回放入的结果
     */
    public void writeErrorMapToRedis(ConcurrentHashMap<String, String> errorMap){
        AleErrorMessageThreadPoolUtil.execute(() -> {
            doWriteErrorMapToRedis(errorMap);
        });
    }


    /**
     * 真正做将业务异常描述全部放入redis数据库中
     * @param errorMap      异常信息map
     */
    private void doWriteErrorMapToRedis(ConcurrentHashMap<String, String> errorMap){
        String errorMessagePrefixNameOne = "";
        // 写入时取出错误信息前缀
        String setErrorMessagePrefixOne = aleErrorMessageBaseProperties.getErrorMessagePrefixNameOne();
        if (null != setErrorMessagePrefixOne){
            errorMessagePrefixNameOne = setErrorMessagePrefixOne;
        }
        String errorMsgPrefix = errorMessagePrefixNameOne + AleErrorMessageConst.ERROR_MSG_PREFIX_TWO;
        // 最终结果
        AtomicReference<String> result = new AtomicReference(AleErrorMessageConst.SUCCESS);
        // 定义批次执行量
        int inBatchNum = 100;
        InBatchUtil.dealDataToMap(inBatchNum, errorMap, batchMap -> {
            // 初始化需要传入lua脚本的参数
            List<String> keyList = new ArrayList<>(inBatchNum);
            List<String> valueList = new ArrayList<>(inBatchNum);
            // lua脚本参数
            StringBuilder script = new StringBuilder();
            // lua脚本中参数的数字
            AtomicInteger index = new AtomicInteger(0);
            // 遍历整合lua脚本以及lua脚本的入参
            batchMap.forEach((status, msg) -> {
                int indexTemp = index.incrementAndGet();
                script.append(String.format(AleErrorMessageRedisLuaTemplateConst.SET, indexTemp, indexTemp));
                keyList.add(errorMsgPrefix + status);
                valueList.add(msg.toString());
            });
            // 定义lua脚本执行的返回参数
            script.append(AleErrorMessageRedisLuaTemplateConst.RETURN_SUCCESS);
            // 执行lua脚本
            Object eval = aleErrorMessageRedisService.eval(script.toString(), keyList, valueList);
            // 多次执行的lua脚本只要有一次报错，最后日志会打印整体结果有误
            // 当redis没有引入的时候，则eval为null
            if (null == eval || !AleErrorMessageRedisLuaTemplateConst.SUCCESS_FLAG.equals(eval.toString())){
                result.set(AleErrorMessageConst.FAIL);
            }
        });
        log.info("初始化业务信息结果为：{}。（如果是FAIL，但是未配置aleErrorMessage所需的redis，请忽略。）", result);
    }

    /**
     * 获取当前生效的语言
     * @return      返回当前生效的语言
     */
    private String getEffectLanguage(){
        // 当前生效语言的变量
        String effectLanguage = "";
        if (null == aleErrorMessageBaseProperties.getEffectLanguage()){
            effectLanguage = AleErrorMessageConst.ERROR_MSG_FILE_DEFAULT_EFFECT_LANGUAGE;
        } else {
            effectLanguage = aleErrorMessageBaseProperties.getEffectLanguage();
        }
        return effectLanguage;
    }

    /**
     * 将所有符合条件的文件全部筛选出来并且将里面的属性封装到一个map并返回
     * @param currentEffectLanguage     当前生效的语言
     * @return      返回所有业务异常的键值对集合
     */
    private ConcurrentHashMap<String, String> getErrorMap(String currentEffectLanguage) {
        // 创建一个返回map
        ConcurrentHashMap<String, String> responseMap = new ConcurrentHashMap<>(1024);

        // 业务消息路径
        String errorMessagePath = "";
        // 如果没有明确指定业务消息路径，则给默认路径
        if (null == aleErrorMessageBaseProperties.getPath()){
            // 获取根项目的 classes 路径
            String rootProjectClassesPath = this.getClass().getResource("/").getPath();
            // 拼接出默认路径
            errorMessagePath = rootProjectClassesPath + AleErrorMessageConst.ERROR_MSG_FILE_DEFAULT_PATH;
        } else {
            errorMessagePath = aleErrorMessageBaseProperties.getPath();
        }

        // 找出当前文件夹下的所有文件
        File file = new File(errorMessagePath);
        // 如果是文件夹才进入，如果不是文件夹，说明路径不符合条件
        if(!file.isDirectory()){
            return new ConcurrentHashMap<>(0);
        }
        File[] files = file.listFiles();
        // 遍历所有文件
        for(File rec : files){
            if(rec.getName().contains(currentEffectLanguage)){
                Map<String, String> currentErrorDescribeMap = null;
                String fileUrl = errorMessagePath + rec.getName();
                try{
                    currentErrorDescribeMap = PropertiesUtil.singleFileLoad(fileUrl);
                }catch (IOException e){
                    log.error("解析aleErrorMessage文件时出现异常，文件名:{}，异常信息为：{}。", fileUrl, e);
                }
                responseMap.putAll(currentErrorDescribeMap);
            }
        }
        return responseMap;
    }
}
